package com.yonyou.pmclouds.workflow.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.billtype.entity.BillTypeVO;
import com.yonyou.pmclouds.businessobject.entity.BoAttrsVO;
import com.yonyou.pmclouds.workflow.entity.ProcessDataSynResultVO;
import com.yonyou.pmclouds.workflow.entity.ProcessDataSynVO;
import com.yonyou.pmclouds.workflow.util.ProcessServiceConst;
import com.yonyou.pmclouds.workflow.util.WorkFlowConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import yonyou.bpm.rest.FormService;
import yonyou.bpm.rest.exception.RestException;
import yonyou.bpm.rest.request.RestVariable;
import yonyou.bpm.rest.request.form.FormFieldQueryParam;
import yonyou.bpm.rest.request.form.FormFieldResourceParam;
import yonyou.bpm.rest.request.form.FormQueryParam;
import yonyou.bpm.rest.request.form.FormResourceParam;

import java.util.*;

/**
 *  同步表单定义和表单字段定义到云审批
 *  @author zhangzhonghao
 */

@Slf4j
@Service
public class FormSynService {

    private static final String FIELD_TYPE_CHAR = "char";

    private static final String FIELD_TYPE_VARCHAR = "varchar";

    private static final String FIELD_TYPE_DATETIME = "datetime";

    private static final String FIELD_TYPE_BYTE = "byte";

    /**
     * 根据单据类型同步表单数据
     *
     * @param processModelId 流程模型ID
     * @return 同步结果
     */
    public ProcessDataSynResultVO formDataSyn(String processModelId, BillTypeVO billTypeVO, String userId,
                                              String tenantId) throws BusinessException {
        FormService formService = getDefaultFormService(userId, tenantId);
        List<ProcessDataSynVO> dataSynVOList = new ArrayList<>();
        // 查询单据类型表获取该单据信息
        if (billTypeVO != null) {
            // 同步之前先删除旧数据
            deleteFormAndField(processModelId, userId, tenantId);
            String tableName = billTypeVO.getTableName();
            // 同步表单信息至云审批
            FormResourceParam resourceParam = new FormResourceParam();
            resourceParam.setModelId(processModelId);
            resourceParam.setCode(processModelId);
            resourceParam.setTitle(billTypeVO.getSname());
            resourceParam.setDescription(billTypeVO.getSname());
            JsonNode jsonNode;
            try {
                jsonNode = (JsonNode) formService.saveForm(resourceParam);
                if (jsonNode == null) {
                    throw new BusinessException(WorkFlowConst.YS_NULL_RESP_MSG);
                }
            } catch (RestException e) {
                throw new BusinessException("同步表单信息至云审批 :" + e.getMessage());
            }
            // 将同步结果插入到日志表中
            ProcessDataSynVO dataSynVO = this.getDataSynVO(userId, tenantId);
            dataSynVO.setDataType(ProcessServiceConst.DATA_TYPE_FORM);
            dataSynVO.setSynResult(String.valueOf(jsonNode.get(ProcessServiceConst.FIELD_ERROR_CODE).intValue()));
            dataSynVO.setSynObject("formCode:" + tableName + "_formName:" + billTypeVO.getSname());
            dataSynVO.setSynMessage(jsonNode.get(ProcessServiceConst.FIELD_ERROR_MESSAGE).textValue());
            dataSynVOList.add(dataSynVO);
            // 同步表单字段至云审批
            this.formFieldSyn(tableName, jsonNode.get(ProcessServiceConst.FIELD_ID).textValue(), processModelId,
                    dataSynVOList, userId, tenantId);
        }
        ProcessDataSynResultVO resultVO = new ProcessDataSynResultVO();
        resultVO.setDataSynVOList(dataSynVOList);
        return resultVO;
    }

    /**
     * 根据表名查询表定义文件获取结构信息，同步至云审批
     *
     * @param tableName      单据对应表名
     * @param formId         表单ID
     * @param processModelId 流程模型ID
     */
    private void formFieldSyn(String tableName, String formId, String processModelId,
                              List dataSynVOList, String userId, String tenantId) throws BusinessException {
        FormService formService = getDefaultFormService(userId, tenantId);

        // 获取表结构信息
        List<BoAttrsVO> attrsVOS = filterField(ProcessVariableProcessor.getAttributeVOs(tableName));
        for (BoAttrsVO attrsVO : attrsVOS) {
            // 同步表单字段信息至云审批
            FormFieldResourceParam resourceParam = new FormFieldResourceParam();
            resourceParam.setCode(attrsVO.getCode());
            resourceParam.setName(attrsVO.getName());
            resourceParam.setFormId(formId);
            resourceParam.setModelId(processModelId);
            resourceParam.setTypeName(attrsVO.getFieldtype());
            // 如果字段可选项值不为空，则将字段类型改为选择项
            if (attrsVO.getFieldOptionMap() != null) {
                Map<String, String> optionMap = attrsVO.getFieldOptionMap();
                List<String> optionNameList = new ArrayList<>();
                for (Map.Entry<String, String> entry : optionMap.entrySet()) {
                    optionNameList.add(entry.getValue());
                }
                resourceParam.setSelectOptions(optionNameList.toArray(new String[]{}));
                resourceParam.setTypeName(FormFieldResourceParam.SELECT_FORMFIELD_TYPE);
            }
            JsonNode jsonNode;
            try {
                jsonNode = (JsonNode) formService.saveFormField(resourceParam);
                if (jsonNode == null) {
                    throw new BusinessException(WorkFlowConst.YS_NULL_RESP_MSG);
                }
            } catch (RestException e) {
                throw new BusinessException("同步表单字段至云审批 :" + e.getMessage());
            }
            // 将同步结果插入到日志表
            ProcessDataSynVO dataSynVO = this.getDataSynVO(userId, tenantId);
            dataSynVO.setDataType(ProcessServiceConst.DATA_TYPE_FORM_FIELD);
            dataSynVO.setSynMessage(jsonNode.get(ProcessServiceConst.FIELD_ERROR_MESSAGE).textValue());
            dataSynVO.setSynObject("fieldCode:" + attrsVO.getCode() + "_fieldName:" + attrsVO.getName());
            dataSynVO.setSynResult(String.valueOf(jsonNode.get(ProcessServiceConst.FIELD_ERROR_CODE).intValue()));
            dataSynVOList.add(dataSynVO);
        }
    }

    /**
     * 云审批只支持字段类型为string,boolean,number,date,select
     *
     * @param attrsVOS 表单字段集合
     * @return 过滤之后结果
     */
    private List<BoAttrsVO> filterField(List<BoAttrsVO> attrsVOS) {
        List<BoAttrsVO> newAttributeVOs = new ArrayList<>();
        for (BoAttrsVO attrsVO : attrsVOS) {
            String fieldType = attrsVO.getFieldtype();
            if (FIELD_TYPE_CHAR.equalsIgnoreCase(fieldType) ||
                    FIELD_TYPE_VARCHAR.equalsIgnoreCase(fieldType) ||
                    RestVariable.STRING_VARIABLE_TYPE.equalsIgnoreCase(fieldType)) {
                attrsVO.setFieldtype(FormFieldResourceParam.STRING_FORMFIELD_TYPE);
                newAttributeVOs.add(attrsVO);
            } else if (RestVariable.INTEGER_VARIABLE_TYPE.equalsIgnoreCase(fieldType) ||
                    RestVariable.LONG_VARIABLE_TYPE.equalsIgnoreCase(fieldType) ||
                    RestVariable.DOUBLE_VARIABLE_TYPE.equalsIgnoreCase(fieldType) ||
                    RestVariable.SHORT_VARIABLE_TYPE.equalsIgnoreCase(fieldType) ||
                    RestVariable.BYTE_ARRAY_VARIABLE_TYPE.equalsIgnoreCase(fieldType) ||
                    FIELD_TYPE_BYTE.equalsIgnoreCase(fieldType) ||
                    ProcessServiceConst.FIELD_TYPE_TINYINT.equalsIgnoreCase(fieldType)) {
                attrsVO.setFieldtype(FormFieldResourceParam.NUMBER_FORMFIELD_TYPE);
                newAttributeVOs.add(attrsVO);
            } else if (RestVariable.BOOLEAN_VARIABLE_TYPE.equalsIgnoreCase(fieldType)) {
                newAttributeVOs.add(attrsVO);
            } else if (RestVariable.DATE_VARIABLE_TYPE.equalsIgnoreCase(fieldType) ||
                    FIELD_TYPE_DATETIME.equalsIgnoreCase(fieldType)) {
                attrsVO.setFieldtype(FormFieldResourceParam.DATE_FORMFIELD_TYPE);
                newAttributeVOs.add(attrsVO);
            } else if (FormFieldResourceParam.SELECT_FORMFIELD_TYPE.equalsIgnoreCase(fieldType)) {
                newAttributeVOs.add(attrsVO);
            }
        }
        return newAttributeVOs;
    }

    /**
     * 删除表单和表单字段
     *
     * @param processModelId 模型ID
     */
    public void deleteFormAndField(String processModelId, String userId,
                                   String tenantId) throws BusinessException {
        FormService formService = getDefaultFormService(userId, tenantId);

        FormQueryParam formQueryParam = new FormQueryParam();
        formQueryParam.setModelId(processModelId);
        try {
            JsonNode formsNode = (JsonNode) formService.queryForms(formQueryParam);
            if (formsNode == null) {
                throw new BusinessException(WorkFlowConst.YS_NULL_RESP_MSG);
            }
            if (formsNode.findValue(ProcessServiceConst.FIELD_DATA) != null &&
                    formsNode.findValue(ProcessServiceConst.FIELD_DATA).size() > 0) {
                String formId = formsNode.findValue(ProcessServiceConst.FIELD_DATA).get(0).findValue(ProcessServiceConst.FIELD_ID).textValue();
                // 查询表单字段
                FormFieldQueryParam fieldQueryParam = new FormFieldQueryParam();
                fieldQueryParam.setFormId(formId);
                JsonNode fieldsNode = (JsonNode) formService.queryFormFields(fieldQueryParam);
                if (fieldsNode == null) {
                    throw new BusinessException(WorkFlowConst.YS_NULL_RESP_MSG);
                }
                // 删除表单字段
                if (fieldsNode.findValue(ProcessServiceConst.FIELD_DATA) != null &&
                        fieldsNode.findValue(ProcessServiceConst.FIELD_DATA).size() > 0) {
                    for (JsonNode fieldNode : fieldsNode.findValue(ProcessServiceConst.FIELD_DATA)) {
                        String fieldId = fieldNode.findValue(ProcessServiceConst.FIELD_ID).textValue();
                        formService.deleteFormField(fieldId);
                    }
                }
                // 删除表单
                formService.deleteForm(formId);
            }
        } catch (RestException e) {
            throw new BusinessException("删除云审批中的表单和表单字段 : " + e.getMessage());
        }
    }

    private ProcessDataSynVO getDataSynVO(String userId, String tenantId) {
        Date currentDate = Calendar.getInstance().getTime();
        ProcessDataSynVO synVO = new ProcessDataSynVO();
        synVO.setPkTenant(tenantId);
        synVO.setCreationtime(currentDate);
        synVO.setModifytime(currentDate);
        synVO.setCreator(userId);
        synVO.setModifier(userId);
        synVO.setLastSynDate(currentDate);
        return synVO;
    }

    private FormService getDefaultFormService(String userId, String tenantId) throws BusinessException {
        return (FormService) ProcessServiceFactory.getProcessService(ProcessServiceFactory.FORM_SERVICE, userId, tenantId);
    }

}
